package mote;

import java.util.ArrayList;
import java.util.Iterator;
import java.lang.String;
import java.lang.StringBuilder;

/**
 * MoteCollection contains a list of all motes currently connected to MoteBridge
 * @author Khalid Alhokail, Vineet Gupta, Oren Melzer, David Schneider
 *
 */
public class MoteCollection {
	private ArrayList<Mote> motes;
	private MoteInterface connection;
	private int darkLevel = 10; //it will be lowered automatically if lower levels are detected
	private int lightLevel = 0; //it will be increased automatically if higher levels are detected
	
	/**
	 * Creates a new empty MoteCollection. Motes can then be added.
	 *
	 */
	public MoteCollection(){
		motes = new ArrayList<Mote>();
		connection = null;
	}

	/**
	 * Sets the connection to the Motes for the collection and all of 
	 * its motes. One example of a connection is is MoteBridge.
	 * @param conn	A MoteInterface that connects to the motes
	 */
	protected void setConnection(MoteInterface conn){
		connection = conn;
		Iterator<Mote> it = motes.iterator();
		while(it.hasNext()){
			Mote cur = it.next();
			cur.setConnection(connection);
		}		
	}
	
	/**
	 * Counts the amount of motes currently in the collection.
	 * @return 	The amount of motes in the current collection
	 */
	public int Count(){
		return motes.size();
	}
	
	/**
	 * Returns the position of the mote with the specified Id. If
	 * the mote does not exist, it returns -1.
	 * @param	moteId	The requested moteId to find
	 * @return	The position of the mote if it is found or -1 if
	 * 			the mote is not found.
	 */
	public int MotePosition(int moteId)
	{
		for(int i = 0; i < Count(); ++i)
		{
			if(motes.get(i).GetId() == moteId)
			{
				return i;
			}
		}
		// 
		// if we've gotten this far, the mote was not found
		//
		return -1;
	}
	
	/**
	 * Determines if the mote currently exists in the collection.
	 * @param moteId	The id of the mote
	 * @return			True if the mote is in the collection, else false
	 */
	public boolean MoteExists(int moteId)
	{
		return (moteById(moteId) != null);
	}
	  
	/**
	 * Returns a copy of the list of motes
	 * @return		The list of motes
	 */	
	public ArrayList<Mote> Motes()
	{
		// implement this once we have a copy constructor in Mote
		return motes;
	}
	
	/**
	 * Returns the nth mote of the collection.
	 * @param index	The index of the mote to be returned
	 * @return		The mote specified by the index. If the index fails
	 * 				to specify a mote, returns null.
	 */
	public Mote getNthMote(int index){
		try {
			return motes.get(index);
		}catch (IndexOutOfBoundsException e){
			return null;
		}
	}
	
	/**
	 * Finds the first mote with the given ID, if one exists, else 
	 * returns null.
	 * @param	moteId	The ID of the mote to find
	 * @return	The mote from the given ID if found, else returns null
	 */
	public Mote moteById(int moteId)
	{
		for(int i = 0; i < Count(); ++i)
		{
			Mote m = motes.get(i);
			if(m.GetId() == moteId)
			{
				return m;
			}
		}
		// 
		// if we've gotten this far, the mote was not found
		//
		return null;
		
	}
	
	/**
	 * Changes the light sensor reading for a given mote.
	 * @param newReading	The new reading for the mote
	 * @param moteId		The moteId that specifies which mote the value should be changed
	 * @return	True if the mote reading is changed. False if the mote is not found.
	 */
	protected boolean changeReading(int newReading, int moteId){
		Mote m = moteById(moteId);
		
		if(newReading > lightLevel)
		{
			lightLevel = newReading;
		}

		if(newReading < darkLevel)
		{
			darkLevel = newReading;
		}
		
		if(null == m)
		{
			return false;
		}
		else
		{
			m.SetReading(newReading);
			return true;
		}
	}
	
	/**
	 * Adds a new mote to the collection if possible. Does not
	 * add a mote if one already exists with the same ID.
	 * @param moteId	The ID of the mote to be added
	 * @return	True if the mote is added successfully. False if there 
	 * 			already exists a mote with the same ID. 
	 */
	protected boolean addMote(int moteId){
		if(!MoteExists(moteId)){
			motes.add(new Mote(moteId, connection));
			return true;
		}
		else {
			return false;
		}
	}
	
	/**
	 * Adds a new mote to the collection if it is unique.
	 * @param m	The new mote to be added
	 * @return	True if the mote is successfully added. False if the mote
	 * 			already exists.
	 */
	protected boolean addMote(Mote m){
		if(motes.contains(m)){
			return false;
		}
		else{
			motes.add(m);
			return true;
		}
	}

	/**
	 * Outputs the mote collection into a String
	 * @return	A string representation of the MoteCollection
	 */
	public String toString(){
		StringBuilder result = new StringBuilder();
		if(motes.size() > 0){
			Iterator<Mote> it = motes.iterator();
			while(it.hasNext()){
				Mote cur = it.next();
				result.append(cur.toString());
				result.append("\n");
			}
		}
		else{
			result.append("No motes detected\n");
		}
		return result.toString();
	}
	
	/**
	 * Sets the LEDs of the given mote. 
	 * @param moteId	The Id of the mote to set
	 * @param LEDs		The LEDs of the mote as a byte. 
	 * <br>	1 for LED 0.
	 * <br>	2 for LED 1.
	 * <br> 4 for LED 2.
	 * <br>For example, 7 is all on, and 0 is all off. 
	 * @return	If the packet was successfully queued to be transfered to the
	 * 			GameHub to be relayed to the mote. Will fail if the mote does
	 * 			not exist or the serial write buffer is full.
	 */
	public boolean setLEDs(int moteId, byte LEDs){
		return moteById(moteId).setLEDs(LEDs);
	}
	
	/**
	 * Gets the highest level of light found
	 * @return	The value of highest level of light found
	 */
	public int LightLevel()
	{
		return lightLevel;
	}
	
	/**
	 * Resets the highest and lowest level of lights 
	 */
	public void ResetLevels()
	{
		lightLevel = 0;
		darkLevel = 10;
	}
	
	/**
	 * Gets the lowest level of light found
	 * @return	The value of lowest level of light found
	 */
	public int DarkLevel()
	{
		return darkLevel;
	}
	
	/**
	 * A main method for testing readings.
	 * @param args
	 * @throws InterruptedException
	 */
	public static void main(String[] args) throws InterruptedException{
		MoteCollection motes = new MoteCollection();
		MoteInterface bridge = null;
		
		bridge = new MoteBridge(motes);	
		
		int min = 1000000;
		int max = 0;
		int reading;
		double adjustedReading;
		byte LEDSettings = 0;
		
		bridge.setSamplingInterval(12, 20);
		
		while(true){
			Iterator<Mote> it = motes.Motes().iterator();
			while(it.hasNext()){
				Mote cur = it.next();
				reading = cur.Reading();
				
				//Some simple calibration
				if(reading > max){
					max = reading;	
				}
				
				if(reading < min){
					min = reading;
				}
				
				adjustedReading = (reading - min) * 100 / ((max - min) > 0 ? (max - min) : 1);
				System.out.print(cur.GetId()+":"+adjustedReading);
				System.out.print("\n");
			}			
			Thread.sleep(250);
			if(bridge != null){
				bridge.setLEDs(12, LEDSettings);
				LEDSettings = (byte)((LEDSettings + 1) % 8);
			}
		}
	}
}
